package edu.hawaii.senin.iclouds.view;

import java.awt.Font;
import java.awt.event.ActionListener;
import java.util.Observable;
import java.util.Observer;

import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.ScrollPaneConstants;
import javax.swing.border.BevelBorder;

import net.miginfocom.swing.MigLayout;
import edu.hawaii.senin.iclouds.basics.PopulationSimple;
import edu.hawaii.senin.iclouds.basics.ReadConfigException;
import edu.hawaii.senin.iclouds.simple.Cloud;
import edu.hawaii.senin.iclouds.simple.CloudParametersMessage;
import edu.hawaii.senin.iclouds.simple.CloudReactions;

/**
 * Provides a graphical user interface to the iClouds project.
 * 
 * @author Pavel Senin
 * 
 */
public class View implements Observer {

  /**
   * Main panel for the GUI.
   */
  private final JFrame frame = new JFrame("iClouds Demo v. 2.0");

  // file configuration
  private final JPanel mainPanel = new JPanel();
  private final JLabel configLabel = new JLabel("Config used:");
  private final JTextField configField = new JTextField("");
  private final JLabel logLabel = new JLabel("Logging into:");
  private final JTextField logField = new JTextField("");

  // tabbed config panel
  private final JTabbedPane tabbedConfigPane = new JTabbedPane();

  private final JPanel cloudTabbedPane = new JPanel();
  private final JLabel cloudTemperatureLabel = new JLabel("Temperature:");
  private final JTextField cloudTemperatureField = new JTextField("");
  private final JLabel cloudDensityLabel = new JLabel("Density:");
  private final JTextField cloudDensityField = new JTextField("");
  private final JLabel dhRatioLabel = new JLabel("D/H ratio:");
  private final JTextField dhRatioField = new JTextField("");
  private final JLabel cloudAccretionStepLabel = new JLabel("Accretion step:");
  private final JTextField cloudAccretionStepField = new JTextField("");
  private final JLabel cloudIterationLimitLabel = new JLabel("Iterations limit:");
  private final JTextField cloudIterationLimitField = new JTextField("");
  private final JLabel cloudBuryLabel = new JLabel("Bury routine:");
  private final JRadioButton buryOnButton = new JRadioButton("bury is on");
  private final JRadioButton buryOffButton = new JRadioButton("bury is off");
  private final JLabel cloudCascadeLabel = new JLabel("Cascade routine:");
  private final JRadioButton cascadeOnButton = new JRadioButton("cascade is on");
  private final JRadioButton cascadeOffButton = new JRadioButton("cascade is off");  


  private final JPanel populationTabbedPane = new JPanel();
  // private TextArea populationTextArea = new TextArea("",50,80,TextArea.SCROLLBARS_NONE);
  private final PopulationTableModel pTableModel = new PopulationTableModel();
  JTable populationTable = new JTable(pTableModel);
  // private JTextArea populationTextArea = new JTextArea();
  JScrollPane populationScrollPane = new JScrollPane(populationTable,
      ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
      ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);

  private final JPanel reactionsTabbedPane = new JPanel();
  private final ReactionTableModel rTableModel = new ReactionTableModel();
  JTable reactionTable = new JTable(rTableModel);
  // private JTextArea reactionsTextArea = new JTextArea();
  JScrollPane reactionsScrollPane = new JScrollPane(reactionTable,
      ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
      ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);

  // buttons pane configuration
  private final JPanel buttonsPane = new JPanel();
  private final JButton loadConfigButton = new JButton("Load config");
  private final JButton saveLogButton = new JButton("Select log");
  private final JButton runSimulationButton = new JButton("RunSimulation");
  private final JButton stopSimulationButton = new JButton("StopSimulation");

  // log panel configuration
  private final JTextArea logTextArea = new JTextArea();
  JScrollPane logTextPane = new JScrollPane(logTextArea,
      ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
      ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
  private final StringBuffer logText = new StringBuffer(10000);

  /**
   * Creates the layout of the GUI window. Uses the MigLayout manager to simplify the display.
   */
  private void configureGUI() {

    // set look and fill
    JFrame.setDefaultLookAndFeelDecorated(true);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    // set the layout for the application
    MigLayout mainFrameLayout = new MigLayout("", "[fill,grow,center]",
        "[fill]10[fill,grow]10[fill]10[fill,grow]");
    this.frame.getContentPane().setLayout(mainFrameLayout);

    // base display panel
    this.frame.getContentPane().add(mainPanel, "wrap");
    mainPanel.setBorder(BorderFactory.createEtchedBorder(BevelBorder.LOWERED));
    MigLayout configurationLayout = new MigLayout("fillx",
        "[left]rel[grow,fill]unrel[left]rel[grow,fill]", "[]5[]5[]");
    this.mainPanel.setLayout(configurationLayout);
    this.mainPanel.add(new JLabel("General iClouds settings:"), "span 4,wrap");
    this.mainPanel.add(configLabel, "");
    this.mainPanel.add(configField, "span 3, grow, wrap");
    configField.setEditable(false);
    this.mainPanel.add(logLabel, "");
    this.mainPanel.add(logField, "span 3, grow, wrap");
    logField.setEditable(false);

    // tabbed configuration pane
    this.frame.getContentPane().add(tabbedConfigPane, "wrap");

    MigLayout cloudTabbedLayout = new MigLayout("fillx", "[left]20[grow,fill][grow,fill]", "");
    cloudTabbedPane.setLayout(cloudTabbedLayout);
    tabbedConfigPane.addTab("Cloud parameters", null, cloudTabbedPane, null);
    this.cloudTabbedPane.add(cloudTemperatureLabel, "");
    this.cloudTabbedPane.add(cloudTemperatureField, "spanx 2, grow, wrap");
    this.cloudTabbedPane.add(cloudDensityLabel, "");
    this.cloudTabbedPane.add(cloudDensityField, "spanx 2, grow, wrap");;
    this.cloudTabbedPane.add(dhRatioLabel, "");
    this.cloudTabbedPane.add(dhRatioField, "spanx 2, grow, wrap");
    this.cloudTabbedPane.add(cloudAccretionStepLabel, "");
    this.cloudTabbedPane.add(cloudAccretionStepField, "spanx 2, grow, wrap");
    this.cloudTabbedPane.add(cloudIterationLimitLabel, "");
    this.cloudTabbedPane.add(cloudIterationLimitField, "spanx 2, grow, wrap");
    // Group the bury radio buttons.    
    this.cloudTabbedPane.add(cloudBuryLabel, "");
    ButtonGroup buryGroup = new ButtonGroup();
    buryOnButton.setActionCommand("buryOn");
    buryOffButton.setActionCommand("buryOff");    
    buryGroup.add(buryOnButton);
    buryOnButton.setSelected(true);
    buryGroup.add(buryOffButton);
    this.cloudTabbedPane.add(buryOnButton, "");
    this.cloudTabbedPane.add(buryOffButton, "wrap");
    // Group the cascade radio buttons.    
    this.cloudTabbedPane.add(cloudCascadeLabel, "");
    ButtonGroup cascadeGroup = new ButtonGroup();
    cascadeOnButton.setActionCommand("cascadeOn");
    cascadeOffButton.setActionCommand("cascadeOff");    
    cascadeGroup.add(cascadeOnButton);
    cascadeOnButton.setSelected(true);
    cascadeGroup.add(cascadeOffButton);
    this.cloudTabbedPane.add(cascadeOnButton, "");
    this.cloudTabbedPane.add(cascadeOffButton, "wrap");    

    tabbedConfigPane.addTab("Cloud Population", null, populationTabbedPane, null);
    MigLayout populationPaneLayout = new MigLayout("", "[grow,fill]", "[grow,fill]");
    populationTabbedPane.setLayout(populationPaneLayout);
    populationTabbedPane.add(populationScrollPane, "height :100:");
    // this.populationTextArea.setEditable(true);
    // this.populationTextArea.append("");
    // this.populationTextArea.setFont(new Font("Monospaced", Font.PLAIN, 12));
    // this.populationTextArea.setCaretPosition(this.populationTextArea.getDocument().getLength());
    populationScrollPane.setBorder(BorderFactory.createEtchedBorder(BevelBorder.LOWERED));
    populationScrollPane.setAutoscrolls(false);

    tabbedConfigPane.addTab("Reactions", null, reactionsTabbedPane, null);
    MigLayout reactionsPaneLayout = new MigLayout("", "[grow,fill]", "[grow,fill]");
    reactionsTabbedPane.setLayout(reactionsPaneLayout);
    reactionsTabbedPane.add(reactionsScrollPane, "height :100:");
    // this.reactionsTextArea.setEditable(false);
    // this.reactionsTextArea.append("");
    // this.reactionsTextArea.setCaretPosition(this.reactionsTextArea.getDocument().getLength());
    reactionsScrollPane.setBorder(BorderFactory.createEtchedBorder(BevelBorder.LOWERED));
    reactionsScrollPane.setAutoscrolls(false);

    // all buttons pane
    this.frame.getContentPane().add(buttonsPane, "wrap");
    MigLayout buttonsPaneLayout = new MigLayout("", "[grow,fill]20[grow,fill]20[grow,fill]", "");
    this.buttonsPane.setLayout(buttonsPaneLayout);
    this.frame.getContentPane().add(buttonsPane, "wrap");
    buttonsPane.setBorder(BorderFactory.createEtchedBorder(BevelBorder.LOWERED));
    // add load config button
    buttonsPane.add(loadConfigButton, "");
    // add save log button
    buttonsPane.add(saveLogButton, "");

    buttonsPane.add(runSimulationButton, "");
    // add stop button
    buttonsPane.add(stopSimulationButton, "wrap");

    // adding logging panel
    this.frame.getContentPane().add(logTextPane, "height :100:");
    this.logTextArea.setFont(new Font("MonoSpaced", Font.PLAIN, 12));
    this.logTextArea.setEditable(false);
    this.logTextArea.append("running iClouds Demo v. 2.0\n");
    this.logTextArea.setCaretPosition(this.logTextArea.getDocument().getLength());
    this.frame.getContentPane().add(logTextPane, "height :100:");
    logTextPane.setBorder(BorderFactory.createEtchedBorder(BevelBorder.LOWERED));
    logTextPane.setAutoscrolls(true);
    this.logText.append("iClouds demo v 1.0 logging:\n");

    // Show frame
    this.frame.pack();
    this.frame.setVisible(true);
  }

  /**
   * Creates an instance of the viewer.
   */
  public View() {
    // does nothing
  }

  /**
   * Obtains this Frame.
   * 
   * @return the Frame.
   */
  public JFrame getJFrame() {
    return this.frame;
  }

  /**
   * Adds the specified load listener.
   * 
   * @param listener the listener to be added.
   */
  public void addLoadListener(ActionListener listener) {
    loadConfigButton.addActionListener(listener);
  }

  /**
   * Adds the log select button listener.
   * 
   * @param listener the listener to be added.
   */
  public void addLogListener(ActionListener listener) {
    saveLogButton.addActionListener(listener);
  }

  /**
   * Adds the RUN listener.
   * 
   * @param listener the listerner to be added.
   */
  public void addRunListener(ActionListener listener) {
    runSimulationButton.addActionListener(listener);
  }

  /**
   * Adds the STOP button listener.
   * 
   * @param listener the listener to be added.
   */
  public void addStopListener(ActionListener listener) {
    stopSimulationButton.addActionListener(listener);
  }

  /**
   * Adds the specified stop listener.
   * 
   * @param listener the listener to be added.
   */
  public void addStopSimulationListener(ActionListener listener) {
    stopSimulationButton.addActionListener(listener);
  }

  /**
   * Shows the GUI.
   */
  public void showGUI() {
    // Schedule a job for the event-dispatching thread:
    // creating and showing this application's GUI.
    javax.swing.SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        configureGUI();
      }
    });
  }

  /**
   * Runs the threads.
   */
  public void run() {
    // empty

  }

  /**
   * Adds a message to the log.
   * 
   * @param str the message to be added to the log.
   */
  public void addToLog(String str) {
    String newStr = str;
    if (str.charAt(str.length() - 1) != '\n') {
      newStr = str.concat("\n");
    }
    this.logTextArea.append(newStr);
    this.logTextArea.setCaretPosition(this.logTextArea.getDocument().getLength());
  }

  /**
   * Reports selected base host.
   * 
   * @return host name.
   */
  public String getHostName() {
    return this.configField.getText();
  }

  /**
   * Updates the status of the observable.
   * 
   * @param o the observable item.
   * @param arg an object used in updating the status.
   * @see java.util.Observer#update(java.util.Observable, java.lang.Object)
   */
  public void update(Observable o, Object arg) {

    if ((o instanceof Cloud) && (arg instanceof String)) {
      String str = arg.toString();
      if (str.charAt(str.length() - 1) != '\n') {
        str = str.concat("\n");
      }
      this.logTextArea.append(str);
      this.logTextArea.setCaretPosition(this.logTextArea.getDocument().getLength());
    }

    if ((o instanceof Cloud) && (arg instanceof CloudParametersMessage)) {
      CloudParametersMessage par = (CloudParametersMessage) arg;
      cloudTemperatureField.setText(par.get(CloudParametersMessage.TEMPERATURE));
      cloudDensityField.setText(par.get(CloudParametersMessage.DENSITY));
      dhRatioField.setText(par.get(CloudParametersMessage.DHRATIO));
      cloudAccretionStepField.setText(par.get(CloudParametersMessage.ACCSTEP));
      cloudIterationLimitField.setText(par.get(CloudParametersMessage.ITRLIMIT));
      this.cloudTabbedPane.updateUI();
      this.cloudTabbedPane.repaint();
    }

    if ((o instanceof Cloud) && (arg instanceof PopulationSimple)) {
      PopulationSimple pop = (PopulationSimple) arg;
      this.pTableModel.setPopulationData(pop);
      // this.populationTabbedPane.updateUI();
      // this.populationTabbedPane.repaint();
    }

    if ((o instanceof Cloud) && (arg instanceof CloudReactions)) {
      CloudReactions rStore = (CloudReactions) arg;
      // for (Reaction r : rStore) {
      this.rTableModel.setReactionData(rStore);
      // this.reactionsTextArea.append(r.toText());
      // this.reactionsTextArea.setCaretPosition(this.reactionsTextArea.getDocument().getLength());
      // }
    }

  }

  /**
   * Sets the config path label.
   * 
   * @param absolutePath config path.
   */
  public void setConfigName(String absolutePath) {
    this.configField.setText(absolutePath);
    this.mainPanel.updateUI();
    this.mainPanel.repaint();
  }

  /**
   * Reports physical parameters set within the GUI.
   * 
   * @return physical clouds parameters.
   */
  public CloudParametersMessage getPhysicalParameters() {

    CloudParametersMessage res = new CloudParametersMessage();

    try {
      res.addParameter(CloudParametersMessage.CFGNAME, this.configField.getText());
      res.addParameter(CloudParametersMessage.LOGNAME, this.logField.getText());
      res.addParameter(CloudParametersMessage.TEMPERATURE, this.cloudTemperatureField.getText());
      res.addParameter(CloudParametersMessage.DENSITY, this.cloudDensityField.getText());
      res.addParameter(CloudParametersMessage.DHRATIO, this.dhRatioField.getText());
      res.addParameter(CloudParametersMessage.ACCSTEP, this.cloudAccretionStepField.getText());
      res.addParameter(CloudParametersMessage.ITRLIMIT, this.cloudIterationLimitField.getText());
      res.addParameter(CloudParametersMessage.BURY, 
          String.valueOf(this.buryOnButton.isSelected()));
      res.addParameter(CloudParametersMessage.CASCADE,
          String.valueOf(this.cascadeOnButton.isSelected()));     
    }
    catch (ReadConfigException e) {
      e.printStackTrace();
    }

    return res;
  }

  /**
   * Sets the log name.
   * 
   * @param absolutePath name and path to display.
   */
  public void setLogName(String absolutePath) {
    this.logField.setText(absolutePath);
    this.mainPanel.updateUI();
    this.mainPanel.repaint();
  }

}
